package eu.catengine.common.manager;

import eu.catengine.common.iface.IDisposable;
import eu.catengine.primitive.Point;

/**
 * Manager odpowiedzialny za przechowywanie danych ekranu.
 * 
 * @author mbienkowski
 */
public class ScreenManager implements IDisposable
{
    private int mScreenWidth = 800;
    private int mScreenHeight = 400;
    private int mOffsetX = 0;
    private int mOffsetY = 0;
    private int mViewportWidth;
    private int mViewportHeight;
    private int mHardwareWidth;
    private int mHardwareHeight;

    private float mViewportScale;

    static ScreenManager mInstance;

    /**
     * Zwraca instanację managera.
     */
    public static ScreenManager getInstance()
    {
	if (mInstance == null)
	{
	    mInstance = new ScreenManager();
	}
	return mInstance;
    }

    /**
     * Zwraca rozmiar marginesu z lewej/prawej strony ekranu.
     */
    public int getOffsetX()
    {
	return mOffsetX;
    }

    /**
     * Zwraca rozmiar marginesu z góry/dołu ekranu.
     */
    public int getOffsetY()
    {
	return mOffsetY;
    }

    /**
     * Zwraca szerokość ekranu określoną przez użytkownika.
     */
    public int getScreenWidth()
    {
	return mScreenWidth;
    }

    /**
     * Zwraca wysokość ekranu określoną przez użytkownika.
     */
    public int getScreenHeight()
    {
	return mScreenHeight;
    }

    /**
     * Zwraca rzeczywistą szerokość ekranu.
     */
    public int getViewportWidth()
    {
	return mViewportWidth;
    }

    /**
     * Zwraca rzeczywistą wysokość ekranu.
     */
    public int getViewportHeight()
    {
	return mViewportHeight;
    }

    /**
     * Ustawia porządaną rozdzielczość ekranu dla gry.
     */
    public void setScreenResolution(int width, int height)
    {
	mScreenWidth = width;
	mScreenHeight = height;
    }

    /**
     * Aktualizuje składowe klasy względem rzeczywistej rozdzielności ekranu.<br>
     * <b>Metoda wywoływana automatycznie.</b> <br>
     * Nie należy wywoływać metody ręcznie.
     */
    public void updateViewportSize(int hardwareWidth, int hardwareHeight)
    {
	this.mHardwareWidth = hardwareWidth;
	this.mHardwareHeight = hardwareHeight;

	this.mViewportScale = Math.min(mHardwareWidth / (float) mScreenWidth, mHardwareHeight / (float) mScreenHeight);

	mViewportWidth = (int) (mScreenWidth * mViewportScale);
	mViewportHeight = (int) (mScreenHeight * mViewportScale);

	mOffsetX = (int) (Math.abs(mHardwareWidth - mViewportWidth) / 2);
	mOffsetY = (int) (Math.abs(mHardwareHeight - mViewportHeight) / 2);
    }

    /**
     * Zwraca punkt sceny na podstawie miejsca dotknięcia ekranu. <br>
     * <b>Metoda wywoływana automatycznie.<b><br>
     * Nie należy wywoływać metody ręcznie.
     * 
     * @param cameraPoint
     *            <code>Point</code> Pozycja kamery.
     * @param touchPoint
     *            <code>Point</code> Punkt dotknięcia ekranu.
     * @return <code>Point</code> Punkt sceny.
     */
    public Point getScenePoint(Point cameraPoint, Point touchPoint)
    {
	return new Point(cameraPoint.x + (touchPoint.x - mOffsetX) / mViewportScale, cameraPoint.y + (mOffsetY - touchPoint.y) / mViewportScale);
    }

    /**
     * Zwraca punkt dla HUD na podstawie miejsca dotknięcia ekranu. <br>
     * <b>Metoda wywoływana automatycznie.<b><br>
     * Nie należy wywoływać metody ręcznie.
     * 
     * @param touchPoint
     *            <code>Point</code> Punkt dotknięcia ekranu.
     * @return <code>Point</code> Punkt HUD.
     */
    public Point getHudPoint(Point touchPoint)
    {
	return new Point((touchPoint.x - mOffsetX) / mViewportScale, mScreenHeight - (touchPoint.y - mOffsetY) / mViewportScale);
    }

    /**
     * Zwraca czy jest możliwość kliknięcia w danym obszarze. <br>
     * <b>Metoda wywoływana automatycznie.<b><br>
     * Nie należy wywoływać metody ręcznie.
     * 
     * @param point
     *            <code>Point</code> Punkt dotknięcia ekranu.
     * @return <code>boolean</code> True, jeśli tak, w przeciwnym razie False.
     */
    public boolean isClickableArea(Point point)
    {
	return (mOffsetX < point.x && point.x < mOffsetX + mViewportWidth && mOffsetY < point.y && point.y < mOffsetY + mViewportHeight);
    }

    @Override
    public void dispose()
    {
	mInstance = null;
    }
}
